package cj.primariaiquest.ws.service;

import java.util.HashMap;
import java.util.Map;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;

import cj.primariaiquest.commons.Pair;
import cj.primariaiquest.integration.endpoints.CanceledTicketGateway;
import cj.primariaiquest.integration.endpoints.TicketGateway;
import cj.primariaiquest.model.Department;
import cj.primariaiquest.model.Ticket;
import cj.primariaiquest.ws.exceptions.InvalidTicketNumber;
import cj.primariaiquest.ws.exceptions.OutOfNumbersException;

import static cj.primariaiquest.model.Department.*;

/**
 * This class is used to get the next ticket number and in the same time it will
 * send it to the message flow.
 * 
 * @author georgian.parvu
 * 
 */
@Service
public class TicketService {
    /**
     * Field used to store the last ticket number for each department.
     */
    private static Map<Department, Long> deparmentAndTicketNumbers;

    /**
     * Field used to store the numbers range for each department.
     */
    private static Map<Department, Pair<Long, Long>> deparmentAndNumberRange;

    /**
     * Static initialization block used to add all departments and initial
     * numbers and departments and initial ranges.
     * 
     */
    static {

	deparmentAndTicketNumbers = new HashMap<Department, Long>();
	deparmentAndTicketNumbers.put(Alimentation, 0L);
	deparmentAndTicketNumbers.put(Fraud, 10L);
	deparmentAndTicketNumbers.put(ThievesProtection, 20L);

	deparmentAndNumberRange = new HashMap<Department, Pair<Long, Long>>();
	deparmentAndNumberRange.put(Alimentation, new Pair<Long, Long>(0L, 9L));
	deparmentAndNumberRange.put(Fraud, new Pair<Long, Long>(10L, 19L));
	deparmentAndNumberRange.put(ThievesProtection, new Pair<Long, Long>(
		20L, 29L));

    }

    private AbstractApplicationContext context;

    public TicketService() {
	context = new ClassPathXmlApplicationContext(
		"/integration/messages-flow.xml", TicketService.class);

    }

    /**
     * Returns the next ticket number for the desired department and places the
     * ticket to the message flow. If none available OutOfNumbersException will
     * be thrown.
     * 
     * @param department
     *            String representing the required department
     * @return long representing the ticket number
     * @throws OutOfNumbersException
     *             if no more numbers are available for the required department
     */
    public long getNextNumber(final Department department)
	    throws OutOfNumbersException {
	long currentTicketNo = deparmentAndTicketNumbers.get(department);

	if (++currentTicketNo <= deparmentAndNumberRange.get(department)
		.getRight()) {
	    deparmentAndTicketNumbers.put(department, currentTicketNo);

	    placeTicket(department, currentTicketNo);

	    return currentTicketNo;
	}
	throw new OutOfNumbersException("Sorry but we ran out of numbers for"
		+ department);
    }

    /**
     * Send ticket to the message flow.
     * 
     * @param department
     *            ticket's department
     * @param currentTicketNo
     *            ticket's number
     */
    private void placeTicket(final Department department,
	    final long currentTicketNo) {

	TicketGateway ticketGateway = (TicketGateway) context
		.getBean("ticketGateway");

	Ticket ticket = new Ticket(department, currentTicketNo);
	ticketGateway.placeTicket(ticket);
    }

    /**
     * Send canceled ticket to the message flow.
     * 
     * @param dep
     *            ticket's department
     * @param number
     *            ticket's number
     * @throws InvalidTicketNumber
     *             if the ticket number provided is invalid
     */
    public void placeCanceledTicket(final Department dep, final long number)
	    throws InvalidTicketNumber {
	CanceledTicketGateway canceledTicketGateway = (CanceledTicketGateway) context
		.getBean("canceledTicketGateway");

	// validate ticket number
	if (number < deparmentAndNumberRange.get(dep).getLeft()
		|| number > deparmentAndNumberRange.get(dep).getRight()) {
	    throw new InvalidTicketNumber(
		    "The ticket number you provided is invalid");
	}
	canceledTicketGateway
		.placeCanceledTicket(new Ticket(dep, number, true));
    }
}
